package org.example.stockdemo.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.example.stockdemo.StockConstant;
import org.example.stockdemo.StockSortTypeEnum;
import org.example.stockdemo.entity.Stock;
import org.example.stockdemo.entity.StockDetail;
import org.example.stockdemo.entity.StockIndustryRank;
import org.example.stockdemo.entity.StockRank;
import org.example.stockdemo.service.TencentStockService;
import org.example.stockdemo.util.StockUtil;
import org.example.stockdemo.vo.FundTrendItemListVO;
import org.example.stockdemo.vo.StockFundFlowVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;
import java.time.LocalDate;
import java.util.*;

@Slf4j
@Service
public class TencentStockServiceImpl implements TencentStockService {

    private static final ObjectMapper objectMapper = new ObjectMapper();
    @Autowired
    private RestTemplate restTemplate;

    private static StockDetail parseStockDetail(String item) {
//        v_s_sh603613="1~国联股份~603613~28.13~0.20~0.72~133751~37547~~202.69~GP-A";
//        v_s_sz300831="51~派瑞股份~300831~14.20~0.20~1.43~40522~5728~~45.44~GP-A-CYB";

        List<String> items = new ArrayList<>();
        String split[] = item.split("~~");
        item = split[0];
        int equalIndex = item.indexOf("~");
        String fieldSubStr, data;
        while (equalIndex != -1) {
            data = item.substring(equalIndex + 1);
            int nextIndex = data.indexOf("~");
            if (nextIndex == -1) {
                nextIndex = data.length();
                equalIndex = -1;
            } else {
                equalIndex = nextIndex;
            }
            fieldSubStr = data.substring(0, nextIndex);
            items.add(fieldSubStr);
            item = data;
        }
        StockDetail stock = new StockDetail();
        stock.setName(items.get(0));
        stock.setCode(items.get(1));
        stock.setPrice(Double.valueOf(items.get(2)));
        stock.setChangeValue(Double.valueOf(items.get(3)));
        stock.setChangePercent(Double.valueOf(items.get(4)));
        stock.setVolume(Double.valueOf(items.get(5)));
        stock.setAmount(Double.valueOf(items.get(6)));
        stock.setMarketValue(split[1].split("~")[0]);
        return stock;
    }

    //    https://proxy.finance.qq.com/cgi/cgi-bin/smartbox/search?stockFlag=1&fundFlag=1&app=official_website&c=1&query=zsdl
    @Override
    public List<Stock> search(String keyword) {

        String url = "https://proxy.finance.qq.com/cgi/cgi-bin/smartbox/search?stockFlag=1&fundFlag=1&app=official_website&c=1&query=" + keyword;
        List<Stock> stocks = null;
        try {
            JSONObject response = restTemplate.getForObject(url, JSONObject.class);
            ArrayList stock = (ArrayList) response.get("stock");
            if (stock == null) {
                return null;
            }
            stocks = new ArrayList<>();
            for (int i = 0; i < stock.size(); i++) {
                LinkedHashMap linkedHashMap = (LinkedHashMap) stock.get(i);
                String code = linkedHashMap.get("code").toString();
                String type = linkedHashMap.get("type").toString();
                if (!code.startsWith(StockConstant.SH) && !code.startsWith(StockConstant.SZ)) {
                    continue;
                }
                if (!type.startsWith(StockConstant.GP) && !type.startsWith(StockConstant.ETF)) {
                    continue;
                }
                code = StockUtil.removeStockPrefix(code);
                String name = linkedHashMap.get("name").toString();
                Stock tempStock = Stock.builder().code(code).name(name).build();
                stocks.add(tempStock);
            }
        } catch (RestClientException e) {
            log.error("获取搜索失败：{}", e.getMessage());
        }
        return stocks;
    }

    @Override
    public StockDetail getRealtimeQuote(String stockCode) {
        stockCode = StockUtil.addStockPrefix(stockCode);
        String url = "https://sqt.gtimg.cn//?q=" + stockCode + "&fmt=json&app=wzq&t=" + System.currentTimeMillis();
        String response = restTemplate.getForObject(url, String.class);
        JSONObject jsonObject = JSONObject.parseObject(response, JSONObject.class);
        JSONArray stockData = (JSONArray) jsonObject.get(stockCode);
        StockDetail stockDetail = new StockDetail();
        stockDetail.setCode(stockCode);
        stockDetail.setName(stockData.getString(1));
        String price = stockData.getString(3);
        stockDetail.setPrice(Double.valueOf(price));
        String yesterdayPrice = stockData.getString(4);
        stockDetail.setYesterdayPrice(Double.valueOf(yesterdayPrice));
        String startPrice = stockData.getString(5);
        stockDetail.setStartPrice(Double.valueOf(startPrice));
        stockDetail.setChangeValue(Double.valueOf(stockData.getString(31)));
        stockDetail.setChangePercent(Double.valueOf(stockData.getString(32)));
        stockDetail.setHighPrice(Double.valueOf(stockData.getString(33)));
        stockDetail.setLowPrice(Double.valueOf(stockData.getString(34)));
        stockDetail.setVolume(Double.valueOf(stockData.getString(36)));
        stockDetail.setAmount(Double.valueOf(stockData.getString(37)));
        return stockDetail;
    }


    /**
     * 批量获取股票实时行情(精简内容)
     *
     * @param stockCodes  股票代码列表
     * @param isAddPrefix
     * @return 股票代码到实时数据的映射
     */
    public List<StockDetail> getBatchRealtimeQuotes(List<String> stockCodes, boolean isAddPrefix) {
        StringBuilder sb = new StringBuilder();
        if (stockCodes == null || stockCodes.isEmpty())
            return null;
        // 遍历stockCodes集合，追加s_元素，然后拼接成字符串
        for (String stockCode : stockCodes) {
            if (StringUtils.isNotBlank(sb)) {
                sb.append(",");
            }
            if (isAddPrefix) {
                if (stockCode.startsWith("00") || stockCode.startsWith("30")) {
                    sb.append("s_sz" + stockCode);
                } else {
                    sb.append("s_sh" + stockCode);
                }
            } else {
                sb.append("s_" + stockCode);
            }
        }

        String url = "http://qt.gtimg.cn/q=" + sb;
        String response = restTemplate.getForObject(url, String.class);
        /*
        v_s_sh603613="1~国联股份~603613~28.13~0.20~0.72~133751~37547~~202.69~GP-A";
        v_s_sz300831="51~派瑞股份~300831~14.20~0.20~1.43~40522~5728~~45.44~GP-A-CYB";
        v_s_sz300502="51~新易盛~300502~365.42~36.18~10.99~614690~2164052~~3632.17~GP-A-CYB";
        */
        String[] items = response.split(";");

        List<StockDetail> stocks = new ArrayList<>();
        for (String item : items) {
            if (StringUtils.isBlank(item.trim())) {
                continue;
            }
            StockDetail stock = parseStockDetail(item);
            stocks.add(stock);
        }
//        stockService.saveStocks(stocks);
        return stocks;
    }


    // 资金净流入排行
    //https://proxy.finance.qq.com/cgi/cgi-bin/rank/hs/getBoardRankList?_appver=11.17.0&board_code=aStock&sort_type=netMainIn&direct=down&offset=0&count=10
//成交额排行
//    https://proxy.finance.qq.com/cgi/cgi-bin/rank/hs/getBoardRankList?_appver=11.17.0&board_code=aStock&sort_type=turnover&direct=down&offset=0&count=20
//    创业板净流入排行
//    https://proxy.finance.qq.com/cgi/cgi-bin/rank/hs/getBoardRankList?_appver=11.17.0&board_code=cyb&sort_type=netMainIn&direct=down&offset=0&count=20
//科创板净流入排行
//    https://proxy.finance.qq.com/cgi/cgi-bin/rank/hs/getBoardRankList?_appver=11.17.0&board_code=ksh&sort_type=netMainIn&direct=down&offset=0&count=20

    @Override
    public List<StockRank> moneyRank(String sortType, String direct, int pageSize) {

        String url = "https://proxy.finance.qq.com/cgi/cgi-bin/rank/hs/getBoardRankList?_appver=11.17.0&board_code=aStock&sort_type=" + sortType +
                "&direct=" + direct + "&offset=0&count=" + pageSize;
        List<StockRank> stockRank = null;
        try {
            JSONObject response = restTemplate.getForObject(url, JSONObject.class);
            LinkedHashMap data = (LinkedHashMap) response.get("data");
            ArrayList<LinkedHashMap> rankList = (ArrayList) data.get("rank_list");
            stockRank = new ArrayList<>();
            StockRank stock;
            int sortOrder = 1;
            String sortName = StockSortTypeEnum.getSortName(sortType, direct);
            for (LinkedHashMap linkedHashMap : rankList) {
                stock = new StockRank();
                stock.setCode(linkedHashMap.get("code").toString());
                stock.setName(linkedHashMap.get("name").toString());
                stock.setPrice(Double.valueOf(linkedHashMap.get("zxj").toString()));
                stock.setChangeValue(Double.valueOf(linkedHashMap.get("zd").toString()));
                stock.setChangePercent(Double.valueOf(linkedHashMap.get("zdf").toString()));
                stock.setVolume(Double.valueOf(linkedHashMap.get("volume").toString()));
                stock.setAmount(Double.valueOf(linkedHashMap.get("turnover").toString()));
                // 涨速
                stock.setSpeed(Double.valueOf(linkedHashMap.get("speed").toString()));
                // 主力流入
                stock.setMainIn(Double.valueOf(linkedHashMap.get("zllr").toString()));
                // 主力流出
                stock.setMainOut(Double.valueOf(linkedHashMap.get("zllc").toString()));
                // 主力净流入
                stock.setMainNetIn(Double.valueOf(linkedHashMap.get("zljlr").toString()));
                stock.setFiveDayChangePercent(Double.valueOf(linkedHashMap.get("zdf_d5").toString()));
                stock.setTenDayChangePercent(Double.valueOf(linkedHashMap.get("zdf_d10").toString()));
                stock.setSortOrder(sortOrder++);
                stock.setSortType(sortType);
                stock.setSortName(sortName);
                stockRank.add(stock);
            }
        } catch (Exception e) {
            log.error("资金净流入排行", e);
        }
        return stockRank;
    }

    /**
     * 板块排行
     *
     * @param sortType
     * @param direct
     * @param pageSize
     * @return
     * @throws IOException
     */
//    https://proxy.finance.qq.com/cgi/cgi-bin/rank/pt/getRank?board_type=hy2&sort_type=priceRatio&direct=up&offset=0&count=10
    @Override
    public List<StockIndustryRank> industryRank(String sortType, String direct, int pageSize) {
        String url = "https://proxy.finance.qq.com/cgi/cgi-bin/rank/pt/getRank?board_type=hy2&sort_type=" + sortType +
                "&direct=" + direct + "&offset=0&count=" + pageSize;

        List<StockIndustryRank> industryRankList = null;
        try {
            JSONObject response = restTemplate.getForObject(url, JSONObject.class);
            LinkedHashMap data = (LinkedHashMap) response.get("data");
            ArrayList<LinkedHashMap> rankList = (ArrayList) data.get("rank_list");
            int sortOrder = 1;
            industryRankList = new ArrayList<>();
            StockIndustryRank stock;
            String sortName = StockSortTypeEnum.getSortName(sortType, direct);
            for (LinkedHashMap linkedHashMap : rankList) {
                stock = new StockIndustryRank();
                stock.setCode(linkedHashMap.get("code").toString());
                stock.setName(linkedHashMap.get("name").toString());
                stock.setPrice(Double.valueOf(linkedHashMap.get("zxj").toString()));
                stock.setChangeValue(Double.valueOf(linkedHashMap.get("zd").toString()));
                stock.setChangePercent(Double.valueOf(linkedHashMap.get("zdf").toString()));

                // 获取领涨股
                LinkedHashMap lzg = (LinkedHashMap) linkedHashMap.get("lzg");
                stock.setTopStockCode(lzg.get("code").toString());
                stock.setTopStockName(lzg.get("name").toString());
                stock.setTopStockPrice(Double.valueOf(lzg.get("zxj").toString()));
                stock.setTopStockChangePercent(Double.valueOf(lzg.get("zdf").toString()));

                stock.setVolume(Double.valueOf(linkedHashMap.get("volume").toString()));
                stock.setAmount(Double.valueOf(linkedHashMap.get("turnover").toString()));
                // 涨速
                stock.setSpeed(Double.valueOf(linkedHashMap.get("speed").toString()));
                // 主力流入
                stock.setMainIn(Double.valueOf(linkedHashMap.get("zllr").toString()));
                // 主力流出
                stock.setMainOut(Double.valueOf(linkedHashMap.get("zllc").toString()));
                // 主力净流入
                stock.setMainNetIn(Double.valueOf(linkedHashMap.get("zljlr").toString()));
                stock.setFiveDayChangePercent(Double.valueOf(linkedHashMap.get("zdf_d5").toString()));
                stock.setTwentyDayChangePercent(Double.valueOf(linkedHashMap.get("zdf_d20").toString()));
                stock.setSortOrder(sortOrder++);
                stock.setSortType(sortType);
                stock.setSortName(sortName);
                industryRankList.add(stock);
            }
        } catch (Exception e) {
            log.error("获取板块排行失败", e);
        }
        return industryRankList;
    }


    /**
     * 获取今日资金流向
     *
     * @param stockCode
     * @return
     */
    @Override
    public StockFundFlowVO getTodayFundFlow(String stockCode) {
        String fullStockCode = StockUtil.addStockPrefix(stockCode);
        String url = "https://proxy.finance.qq.com/cgi/cgi-bin/fundflow/hsfundtab?code=" + fullStockCode + "&type=todayFundFlow&klineNeedDay=20";
        JSONObject response = restTemplate.getForObject(url, JSONObject.class);
        LinkedHashMap code = (LinkedHashMap) response.get("code");
        if (!"0".equals(code.get("code").toString())) {
            log.error("获取股票获取今日资金流向数据失败：{}", code.get("msg"));
            return null;
        }
        LinkedHashMap data = (LinkedHashMap) response.get("data");
        LinkedHashMap todayFundFlowMap = (LinkedHashMap) data.get("todayFundFlow");
        StockFundFlowVO stockFundFlowVO = parseTodayFundFlow(todayFundFlowMap);
        return stockFundFlowVO;
    }

    /**
     * 获取股票1分时线数据
     *
     * @param stockCode
     * @return
     */
    @Override
    public FundTrendItemListVO getMinuteData(String stockCode) {
        // 追加股票前缀
        String fullStockCode = StockUtil.addStockPrefix(stockCode);

        // 获取股票1分时线数据
        String url = "https://proxy.finance.qq.com/cgi/cgi-bin/fundflow/hsfundtab?code=" + fullStockCode + "&type=todayFundTrend&klineNeedDay=20";
        JSONObject response = restTemplate.getForObject(url, JSONObject.class);
        Integer code = (Integer) response.get("code");
        if (code != 0) {
            log.error("获取股票1分时线数据失败：{}", response.get("msg"));
            return null;
        }

        LinkedHashMap data = (LinkedHashMap) response.get("data");
        LinkedHashMap todayFundTrendMap = (LinkedHashMap) data.get("todayFundTrend");
        return parseTodayFundTrend(todayFundTrendMap);
    }


    /**
     * 获取前10天的K线数据
     *
     * @param stockCode
     * @param day = 10
     * @return
     */
    @Override
    public List<StockDetail> getKlineData(String stockCode, int day) {
        // 追加股票前缀
        String fullStockCode = StockUtil.addStockPrefix(stockCode);

        // 一起查询才能获取今日的数据
        String url = "https://proxy.finance.qq.com/cgi/cgi-bin/fundflow/hsfundtab?code=" + fullStockCode + "&type=historyFundFlow,fiveDayFundFlow,todayFundFlow&klineNeedDay="+day;
        JSONObject response = restTemplate.getForObject(url, JSONObject.class);
        Integer code = (Integer) response.get("code");
        if (code != 0) {
            log.error("获取股票历史K线数据失败：{}", response.get("msg"));
            return null;
        }

        LinkedHashMap data = (LinkedHashMap) response.get("data");
        LinkedHashMap todayFundTrendMap = (LinkedHashMap) data.get("historyFundFlow");
        if (CollectionUtils.isEmpty(todayFundTrendMap)) {
            return null;
        }
        ArrayList<LinkedHashMap> oneDayKlineList = (ArrayList<LinkedHashMap>) todayFundTrendMap.get("oneDayKlineList");
        List<StockDetail> stockList = new ArrayList<>();
        for (LinkedHashMap linkedHashMap : oneDayKlineList) {
            StockDetail stock = new StockDetail();
            stock.setCode(stockCode);
            // 修复类型不匹配的问题，将Object转换为LocalDate
            String dateStr = (String) linkedHashMap.get("date");
            stock.setCreatedAt(LocalDate.parse(dateStr));
            stock.setPrice(Double.valueOf(linkedHashMap.get("price").toString()));
            stock.setMainNetIn(Double.valueOf(linkedHashMap.get("mainNetIn").toString()));
            stockList.add(stock);
        }
        return stockList;
    }


    /**
     * 股票资金流动数据详情，1分钟数据
     *
     * @param todayFundTrendMap
     * @return
     */
    public FundTrendItemListVO parseTodayFundTrend(LinkedHashMap todayFundTrendMap) {
        if (CollectionUtils.isEmpty(todayFundTrendMap)) {
            return null;
        }
        ArrayList<LinkedHashMap> minList = (ArrayList<LinkedHashMap>) todayFundTrendMap.get("minList");

        FundTrendItemListVO fundTrendItemListVO = new FundTrendItemListVO();
        List<String> timeList = new ArrayList<>();
        List<Double> priceList = new ArrayList<>();
        // 资金流向
        List<Double> mainNetInflowList = new ArrayList<>();
        // 超大单
        List<Double> uperNetInflowList = new ArrayList<>();
        // 大单
        List<Double> bigNetInflowList = new ArrayList<>();
        // 中单 (4~5W-20W = 散户)
        List<Double> normalNetInflowList = new ArrayList<>();
        // 小单 (0~4W\5W)
        List<Double> smallNetInflowList = new ArrayList<>();
        for (LinkedHashMap itemMap : minList) {

            String time = (String) itemMap.get("time");
            timeList.add(time.substring(8));
            priceList.add(getDoubleValue(itemMap.get("Price")));
            mainNetInflowList.add(getDoubleValue(itemMap.get("MainNetInflow")));
            uperNetInflowList.add(getDoubleValue(itemMap.get("SuperNetInflow")));
            bigNetInflowList.add(getDoubleValue(itemMap.get("BigNetInflow")));
            normalNetInflowList.add(getDoubleValue(itemMap.get("NormalNetInflow")));
            smallNetInflowList.add(getDoubleValue(itemMap.get("SmallNetInflow")));

            /*
            FundTrendItemVO item = new FundTrendItemVO();
            item.setTime((String) itemMap.get("time"));
            item.setPrice(getDoubleValue(itemMap.get("Price")));
            item.setMainNetInflow(getDoubleValue(itemMap.get("MainNetInflow")));
            item.setRetailNetInflow(getDoubleValue(itemMap.get("RetailNetInflow")));
            item.setSuperNetInflow(getDoubleValue(itemMap.get("SuperNetInflow")));
            item.setBigNetInflow(getDoubleValue(itemMap.get("BigNetInflow")));
            item.setNormalNetInflow(getDoubleValue(itemMap.get("NormalNetInflow")));
            item.setSmallNetInflow(getDoubleValue(itemMap.get("SmallNetInflow")));
            item.setMainInflow(getDoubleValue(itemMap.get("MainInflow")));
            item.setMainOutflow(getDoubleValue(itemMap.get("MainOutflow")));
            fundTrendItemListVO.add(item);
            */
        }
        fundTrendItemListVO.setTimeList(timeList);
        fundTrendItemListVO.setPriceList(priceList);
        fundTrendItemListVO.setMainNetInflow(mainNetInflowList);
        fundTrendItemListVO.setSuperNetInflow(uperNetInflowList);
        fundTrendItemListVO.setBigNetInflow(bigNetInflowList);
        fundTrendItemListVO.setNormalNetInflow(normalNetInflowList);
        fundTrendItemListVO.setSmallNetInflow(smallNetInflowList);

        return fundTrendItemListVO;
    }

    /**
     * 安全地将对象转换为Double类型
     *
     * @param value
     * @return
     */
    private Double getDoubleValue(Object value) {
        if (value == null) {
            return null;
        }
        if (value instanceof Double) {
            return (Double) value;
        }
        try {
            return Double.valueOf(value.toString());
        } catch (NumberFormatException e) {
            return -1D;
        }
    }

    /**
     * 股票资金流动
     *
     * @param todayFundFlowMap
     * @return
     */
    public StockFundFlowVO parseTodayFundFlow(LinkedHashMap todayFundFlowMap) {
        if (CollectionUtils.isEmpty(todayFundFlowMap)) {
            return null;
        }
        String json = JSON.toJSONString(todayFundFlowMap);
        StockFundFlowVO stockFundFlowVO = JSONObject.parseObject(json, StockFundFlowVO.class);
        return stockFundFlowVO;
    }

}